﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using Assist;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using YArchitech.Controls;
using YArchitech.Revit;
using YArchitech.Revit.RevitExtension;
using YJKArchUtils.Utils;
using YJKRevitBase.WinForm;
using YJKRevitDimensionText.DimCreator;
using YJKRevitDimensionText.Forms;
using YJKRevitView;

namespace YJKRevitDimensionText.Commands
{
	[Regeneration(RegenerationOption.Manual)]
	[Transaction(TransactionMode.Manual)]
	public class CmdTwoPointDim : YJKCommandManual
	{
		[DllImport("user32.dll", EntryPoint = "SendMessageA")]
		public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);

		protected override Result YJKExcute(ExternalCommandData cmdData, ref string message, ElementSet elements)
		{
            if (SwitchView.SetUsableView((CmdViewType)9, cmdData) != Autodesk.Revit.UI.Result.Succeeded)
			{
				return Autodesk.Revit.UI.Result.Failed;
			}
			if (AssistFunc.GetCurrentViewLevel(cmdData.Application.ActiveUIDocument.Document) == null)
			{
				return Autodesk.Revit.UI.Result.Failed;
			}
            WindowHandle windowHandle = new WindowHandle(Process.GetCurrentProcess().MainWindowHandle);
			DlgTwoPointDim dlgTwoPointDim = new DlgTwoPointDim();
			dlgTwoPointDim.Show(windowHandle);
			int wMsg = 7;
			CmdTwoPointDim.SendMessage(windowHandle.Handle, wMsg, IntPtr.Zero, IntPtr.Zero);
			Transaction transaction = new Transaction(this.m_doc, "TwoPointDim");
			transaction.Start();
			try
			{
                while ((int)this.TwoPointDim(dlgTwoPointDim) != 1)
				{
				}
			}
			catch (Exception ex)
			{
				YJKMessageBox.Show(ex.Message);
			}
			transaction.Commit();
			dlgTwoPointDim.Close();
			return base.YJKExcute(cmdData, ref message, elements);
		}

		public Result TwoPointDim(DlgTwoPointDim dlg)
		{
			XYZ xyz = null;
			XYZ xyz2 = null;
			XYZ xyz3 = null;
			if (DimPublicFunc.PickPoint(this.m_uiDoc, "穿过平行墙/轴线的起点P1：", ref xyz) != Autodesk.Revit.UI.Result.Succeeded)
			{
				return Autodesk.Revit.UI.Result.Cancelled;
			}
			if (DimPublicFunc.PickPoint(this.m_uiDoc, "穿过平行墙/轴线的终点P2：", ref xyz2) != Autodesk.Revit.UI.Result.Succeeded)
			{
				return Autodesk.Revit.UI.Result.Cancelled;
			}
			if (DimPublicFunc.PickPoint(this.m_uiDoc, "标注位置P3：", ref xyz3) != Autodesk.Revit.UI.Result.Succeeded)
			{
				return Autodesk.Revit.UI.Result.Cancelled;
			}
			xyz2 = new XYZ(xyz2.X, xyz2.Y, xyz.Z);
			xyz3 = new XYZ(xyz3.X, xyz3.Y, xyz.Z);
			if (xyz.DistanceTo(xyz2) < YJKCommandManual.precision)
			{
				YJKMessageBox.Show("起终点不能相距太近，请重新点取。");
				return Autodesk.Revit.UI.Result.Succeeded;
			}
			if (Geometry.CalculateFootPoint(xyz, xyz2, xyz3).DistanceTo(xyz3) < YJKCommandManual.precision)
			{
				YJKMessageBox.Show("标注位置不能与起终点同线。");
				return Autodesk.Revit.UI.Result.Succeeded;
			}
			List<Element> crossWallAndAxis = this.GetCrossWallAndAxis(xyz, xyz2);
			List<Line> borderLine = this.GetBorderLine(crossWallAndAxis, xyz, xyz2);
			if (borderLine == null || borderLine.Count < 1)
			{
				YJKMessageBox.Show("起点P1到终点P2之间没有墙或轴线。");
				return Autodesk.Revit.UI.Result.Succeeded;
			}
			XYZ pt = this.MinPoint(xyz, xyz2, xyz3);
			XYZ pt2 = this.MaxPoint(xyz, xyz2, xyz3);
			List<Element> list = DimPublicFunc.ElementsAtBoundingBox(this.m_doc, pt, pt2, BuiltInCategory.OST_Walls);
			if (list == null || list.Count < 1)
			{
				YJKMessageBox.Show("范围内没有墙，不能标注。");
				return Autodesk.Revit.UI.Result.Succeeded;
			}
			List<Wall> list2 = new List<Wall>();
			foreach (Element element in list)
			{
				list2.Add(element as Wall);
			}
			DimPublicFunc.ClearSurfaceWall(ref list2);
			List<ReferenceInfo> list3 = new List<ReferenceInfo>();
			Line line = borderLine[0];
			XYZ xyz4 = Geometry.RotateTo(line.Direction, Math.PI*.5, XYZ.BasisZ);
			List<Grid> list4 = DimPublicFunc.LineCrossGrids(this.m_uiApp, xyz, xyz2, false);
			if (list4 != null)
			{
				Line crossLine = YJKLineEx.YJKGetBound(xyz, xyz + xyz4 * xyz.DistanceTo(xyz2));
				foreach (Grid grid in list4)
				{
					DimPublicFunc.GetGridDimRef(this.m_uiApp, grid, crossLine, ref list3);
				}
			}
			XYZ xyz5 = Geometry.CalculateFootPoint(xyz3, xyz3 + xyz4 * 10.0, xyz);
			XYZ xyz6 = Geometry.CalculateFootPoint(xyz3, xyz3 + xyz4 * 10.0, xyz2);
			Line dimLine = null;
			try
			{
				dimLine = YJKLineEx.YJKGetBound(xyz5, xyz6);
			}
			catch (Exception)
			{
				YJKMessageBox.Show("起点P1到终点P2之间没有墙或轴线。");
				return Autodesk.Revit.UI.Result.Succeeded;
			}
			this.GetWallsRefInfos(list2, dimLine, dlg.WallDimPos, ref list3);
			if (list3.Count < 2)
			{
				YJKMessageBox.Show("在标注方向上没有合适的标注对象。");
				return Autodesk.Revit.UI.Result.Succeeded;
			}
			XYZ xyz7 = DimPublicFunc.To2d(xyz);
			XYZ xyz8 = Geometry.CalculateFootPoint(xyz7, xyz7 + xyz4 * 10.0, DimPublicFunc.To2d(xyz2));
			for (int i = 0; i < list3.Count; i++)
			{
				ReferenceInfo referenceInfo = list3[i];
				referenceInfo.ThePosition = Geometry.CalculateFootPoint(xyz7, xyz8, referenceInfo.ThePosition);
				list3[i] = referenceInfo;
			}
			list3.Sort(new DistanceComparer(xyz7));
			for (int j = 0; j < list3.Count; j++)
			{
				ReferenceInfo referenceInfo2 = list3[j];
				for (int k = j + 1; k < list3.Count; k++)
				{
					if (referenceInfo2.ThePosition.DistanceTo(list3[k].ThePosition) < 0.0032)
					{
						list3.RemoveAt(k);
						k--;
					}
				}
			}
			for (int l = 0; l < list3.Count; l++)
			{
				if (!Geometry.Is_Point_OnSegment(xyz7, xyz8, list3[l].ThePosition, 1E-09))
				{
					list3.RemoveAt(l);
					l--;
				}
			}
			if (list3.Count < 2)
			{
				YJKMessageBox.Show("没有适合的标注参照。");
				return Autodesk.Revit.UI.Result.Succeeded;
			}
			ReferenceArray referenceArray = new ReferenceArray();
			foreach (ReferenceInfo referenceInfo3 in list3)
			{
				referenceArray.Append(referenceInfo3.TheReference);
			}
			XYZ xyz9 = Geometry.CalculateFootPoint(line, xyz);
			XYZ vtDim = (Geometry.CalculateFootPoint(line, xyz3) - xyz9).Normalize();
			Dimension dimension = DimPublicFunc.CreateLinearDim(this.m_doc, referenceArray, xyz3, vtDim, 0);
			if (dimension != null)
			{
				DimPublicFunc.SetDimFlag(dimension, "TwoPointDim");
			}
			return Autodesk.Revit.UI.Result.Succeeded;
		}

		private XYZ MinPoint(XYZ pt1, XYZ pt2, XYZ pt3)
		{
			double num = Math.Min(Math.Min(pt1.X, pt2.X), pt3.X);
			double num2 = Math.Min(Math.Min(pt1.Y, pt2.Y), pt3.Y);
			double num3 = Math.Min(Math.Min(pt1.Z, pt2.Z), pt3.Z);
			return new XYZ(num, num2, num3);
		}

		private XYZ MaxPoint(XYZ pt1, XYZ pt2, XYZ pt3)
		{
			double num = Math.Max(Math.Max(pt1.X, pt2.X), pt3.X);
			double num2 = Math.Max(Math.Max(pt1.Y, pt2.Y), pt3.Y);
			double num3 = Math.Max(Math.Max(pt1.Z, pt2.Z), pt3.Z);
			return new XYZ(num, num2, num3);
		}

		private List<Element> GetCrossWallAndAxis(XYZ pt1, XYZ pt2)
		{
			List<Element> list = new List<Element>();
			List<Element> list2 = DimPublicFunc.ElementsAtBoundingBox(this.m_doc, pt1, pt2, BuiltInCategory.OST_Walls);
			List<Grid> list3 = DimPublicFunc.LineCrossGrids(this.m_uiApp, pt1, pt2, false);
			if (list2 != null)
			{
				list.AddRange(list2);
			}
			if (list3 != null)
			{
				list.AddRange(list3);
			}
			return list;
		}

		private List<Line> GetBorderLine(List<Element> lstCrossElement, XYZ ptCross1, XYZ ptCross2)
		{
			if (lstCrossElement == null)
			{
				return null;
			}
			List<Line> list = new List<Line>();
			if (lstCrossElement.Count == 1)
			{
				Line line = this.GetElemLine(lstCrossElement[0]);
				line = DimPublicFunc.ChangeLineElevation(line, ptCross1.Z);
				list.Add(line);
			}
			else if (lstCrossElement.Count > 1)
			{
				XYZ parallelVector = this.GetParallelVector(lstCrossElement);
				if (parallelVector == null)
				{
					return null;
				}
				Line line2 = null;
				Line line3 = null;
				this.GetOutSideLine(lstCrossElement, ptCross1, ptCross2, parallelVector, ref line2, ref line3);
				if (line2 != null && line3 != null)
				{
					list.Add(line2);
					list.Add(line3);
				}
			}
			return list;
		}

		private void GetOutSideLine(List<Element> lstCrossElement, XYZ ptCross1, XYZ ptCross2, XYZ vtParal, ref Line lineL, ref Line lineR)
		{
			XYZ xyz = null;
			XYZ xyz2 = null;
			Line line = YJKLineEx.YJKGetBound(ptCross1, ptCross2);
			foreach (Element elem in lstCrossElement)
			{
				Line line2 = this.GetElemLine(elem);
				if (!(line2 == null) && DimPublicFunc.IsParallel(line2.Direction, vtParal))
				{
					line2 = DimPublicFunc.ChangeLineElevation(line2, ptCross1.Z);
					XYZ xyz3 = Geometry.IntersectWithTwoCurves(line, line2, ptCross1, false, false, this.m_app);
					if (xyz3 != null)
					{
						if (xyz == null)
						{
							xyz = xyz3;
							lineL = line2;
						}
						else if (xyz2 == null)
						{
							xyz2 = xyz3;
							lineR = line2;
						}
						else if (DimPublicFunc.ExtendLine(xyz3, ref xyz, ref xyz2))
						{
							if (xyz3.DistanceTo(xyz) < YJKCommandManual.precision)
							{
								lineL = line2;
							}
							else
							{
								lineR = line2;
							}
						}
					}
				}
			}
		}

		private XYZ GetParallelVector(List<Element> lstCrossElement)
		{
			XYZ xyz = null;
			for (int i = 0; i < lstCrossElement.Count - 1; i++)
			{
				Line elemLine = this.GetElemLine(lstCrossElement[i]);
				if (!(elemLine == null))
				{
					for (int j = i + 1; j < lstCrossElement.Count; j++)
					{
						Line elemLine2 = this.GetElemLine(lstCrossElement[j]);
						if (!(elemLine2 == null) && DimPublicFunc.IsParallel(elemLine.Direction, elemLine2.Direction))
						{
							xyz = elemLine.Direction;
							break;
						}
					}
					if (xyz != null)
					{
						break;
					}
				}
			}
			return xyz;
		}

		private Line GetElemLine(Element elem)
		{
			Line result = null;
			if (elem is Wall)
			{
				LocationCurve locationCurve = (elem as Wall).Location as LocationCurve;
				if (locationCurve.Curve.GetType() == typeof(Line))
				{
					result = (locationCurve.Curve as Line);
				}
			}
			else if (elem is Grid)
			{
				Grid grid = elem as Grid;
				if (grid.Curve.GetType() == typeof(Line))
				{
					result = (grid.Curve as Line);
				}
			}
			return result;
		}

		private void GetWallsRefInfos(List<Wall> lstAllWall, Line dimLine, int nCrossWallDimType, ref List<ReferenceInfo> lstRefInfo)
		{
			foreach (Wall wall in lstAllWall)
			{
				Line elemLine = this.GetElemLine(wall);
				if (!(elemLine == null))
				{
					if (DimPublicFunc.IsParallel(elemLine.Direction, dimLine.Direction))
					{
						this.GetNearLineRefInfos(wall, dimLine, nCrossWallDimType, lstAllWall, ref lstRefInfo);
					}
					else if (DimPublicFunc.IsVertical(elemLine.Direction, dimLine.Direction))
					{
						this.GetWallCrossRefInfo(wall, dimLine, 0.0, nCrossWallDimType, ref lstRefInfo);
					}
				}
			}
		}

		private void GetNearLineRefInfos(Wall wall, Line dimLine, int nCrossWallDimType, List<Wall> lstAllWall, ref List<ReferenceInfo> lstRefInfo)
		{
			Line line = this.GetElemLine(wall);
			List<ReferenceInfo> list = new List<ReferenceInfo>();
			DimPublicFunc.GetWallDimRef(this.m_uiApp, wall, line, false, ref list, true);
			line = DimPublicFunc.ExtendLine(line, 2, wall.Width);
			this.GetCrossWallsRefInfo(lstAllWall, line, wall.Width, nCrossWallDimType, ref list);
			List<Element> columnsOnLine = this.GetColumnsOnLine(RevitDocumentExtension.GetEndPoint(line, 0), RevitDocumentExtension.GetEndPoint(line, 1));
			if (columnsOnLine != null && columnsOnLine.Count > 0)
			{
				foreach (Element elem in columnsOnLine)
				{
					DimPublicFunc.GetElementDimRef(this.m_uiApp, elem, line, ref list);
				}
			}
			foreach (ReferenceInfo referenceInfo in list)
			{
				XYZ thePosition = referenceInfo.ThePosition;
				XYZ xyz = new XYZ(thePosition.X, thePosition.Y, RevitDocumentExtension.GetEndPoint(dimLine, 0).Z);
				XYZ xyz2 = Geometry.CalculateFootPoint(dimLine, xyz);
				XYZ xyz3 = (xyz2 - xyz).Normalize();
				xyz += xyz3 * wall.Width;
				List<Wall> list2 = DimPublicFunc.LineCrossWalls(this.m_uiApp, lstAllWall, xyz, xyz2);
				if (list2 == null || list2.Count < 1)
				{
					lstRefInfo.Add(referenceInfo);
				}
			}
		}

		private List<Element> GetColumnsOnLine(XYZ pt1, XYZ pt2)
		{
			XYZ xyz = new XYZ(Math.Min(pt1.X, pt2.X), Math.Min(pt1.Y, pt2.Y), pt1.Z);
			XYZ xyz2 = new XYZ(Math.Max(pt1.X, pt2.X), Math.Max(pt1.Y, pt2.Y), pt1.Z + 30.0);
			BoundingBoxIntersectsFilter boundingBoxIntersectsFilter = new BoundingBoxIntersectsFilter(new Outline(xyz, xyz2));
			FilteredElementCollector filteredElementCollector = new FilteredElementCollector(this.m_doc);
			FilteredElementCollector filteredElementCollector2 = new FilteredElementCollector(this.m_doc);
			List<Element> list = new List<Element>();
			List<Element> list2 = filteredElementCollector.OfCategory(BuiltInCategory.OST_Columns).WherePasses(boundingBoxIntersectsFilter).ToList<Element>();
			List<Element> list3 = filteredElementCollector2.OfCategory(BuiltInCategory.OST_StructuralColumns).WherePasses(boundingBoxIntersectsFilter).ToList<Element>();
			if (list2 != null)
			{
				list.AddRange(list2);
			}
			if (list3 != null)
			{
				list.AddRange(list3);
			}
			for (int i = 0; i < list.Count; i++)
			{
				if (list[i].LevelId != this.m_doc.ActiveView.GenLevel.Id)
				{
					list.RemoveAt(i);
					i--;
				}
			}
			return list;
		}

		private void GetCrossWallsRefInfo(List<Wall> lstAllWall, Line crossLine, double dExtendLen, int nBorderOrCenter, ref List<ReferenceInfo> lstRefInfo)
		{
			foreach (Wall wall in lstAllWall)
			{
				this.GetWallCrossRefInfo(wall, crossLine, dExtendLen, nBorderOrCenter, ref lstRefInfo);
			}
		}

		private void GetWallCrossRefInfo(Wall wall, Line crossLine, double dExtendLen, int nBorderOrCenter, ref List<ReferenceInfo> lstRefInfo)
		{
			Line line = this.GetElemLine(wall);
			if (line == null)
			{
				return;
			}
			if (!DimPublicFunc.IsVertical(crossLine.Direction, line.Direction))
			{
				return;
			}
			XYZ hyendPoint = RevitDocumentExtension.GetEndPoint(crossLine, 0);
			line = DimPublicFunc.ChangeLineElevation(line, hyendPoint.Z);
			line = DimPublicFunc.ExtendLine(line, 2, dExtendLen);
			XYZ xyz = Geometry.IntersectWithTwoCurves(crossLine, line, hyendPoint, false, false, this.m_app);
			if (xyz == null)
			{
				return;
			}
			if (nBorderOrCenter == 0)
			{
                DimPublicFunc.GetWallThickRef(this.m_uiApp, wall, crossLine, (Autodesk.Revit.DB.ViewDetailLevel)1, ref lstRefInfo);
				return;
			}
			ReferenceInfo referenceInfo = new ReferenceInfo(DimPublicFunc.GetWallCenterReference(this.m_uiApp, wall), DimPublicFunc.To2d(xyz));
			if (!DimPublicFunc.HasExistRefInfo(lstRefInfo, referenceInfo))
			{
				lstRefInfo.Add(referenceInfo);
			}
		}
	}
}
